package com.uzhie.web.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.uzhie.web.dao.VehicleDAO;
import com.uzhie.web.dao.VideoDAO;
import com.uzhie.exception.MonitorException;
import com.uzhie.jt808.jt1078.entity.*;
import com.uzhie.jt808.JT808Constant;
import com.uzhie.jt808.service.JT808Service;
import com.uzhie.media.MediaConstant;
import com.uzhie.media.service.MediaPostServiceImpl;
import com.uzhie.web.module.Vehicle;
import com.uzhie.web.module.Video;
import com.uzhie.web.module.VideoMonitorReport;
import com.uzhie.util.ConcurrentCollection;
import com.uzhie.util.ConstantKit;
import com.uzhie.util.RedisCacheManager;
import org.apache.log4j.Logger;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.validation.Valid;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutionException;

/**
 * 程复播服务类
 * <p>
 * Created by lilongfei on 2018/7/30.
 */
@Service
public class PlayBackService {
    private Logger logger = Logger.getLogger(this.getClass());
    @Autowired(required = false)
    private JT808Service jt808Service;
    @Autowired
    private VideoDAO videoDAO;
    @Autowired
    private VehicleDAO vehicleDAO;
    @Autowired(required = false)
    private MediaPostServiceImpl<Integer> mediaPostService;
    @Autowired(required = false)
    RedisCacheManager<VideoMonitorReport> redisCacheManager;


    //0 全部,1 主码流 ,2 子码流
    Integer[] streams2 = new Integer[]{0, 1, 2};
    //远程存储位置 0 全部 1 主存储器 2 灾备存储器
    Integer[] eqTypes = new Integer[]{0, 1, 2};
    //音视频资源类型 0 音视频 1 音频 2 视频 3 视频或音频
    Integer[] resources = new Integer[]{0, 1, 2, 3};
    //快进或快退倍数 回放类型为1 2 时,该字段有效,否则默认0   0 无效  1 1倍 2 2倍 3 4倍 4 8倍 5 16倍
    Integer[] multiples = new Integer[]{0, 1, 2, 3, 4, 5};
    //回放方式 0 正常 1 快进 2 关键帧快退  3 关键帧播放 4 单帧上传
    Integer[] backTypes = new Integer[]{0, 1, 2, 3, 4};
    //上传条件 1 wifi上传 2LAN上传 4 3G/4G上传
    Integer[] conditions = new Integer[]{1, 2, 4};
    //远程复播控制 0 开始回放 1 暂停回放 2 结束回放 3 快进回放 4 关键帧快退回放 5 拖动回放 6 关键帧播放
    Integer[] controls = new Integer[]{0, 1, 2, 3, 4, 5, 6};


    /**
     * 查询历史视频的目录
     *
     * @param vehicleNumber 车辆内置sim卡号
     * @param channel       通道号
     * @param startDate     查询起始时间 格式 yyyy-MM-dd HH:mm:ss
     * @param endDate       查询截止时间 格式 yyyy-MM-dd HH:mm:ss
     * @param warning       告警标识
     * @param resource      音视频资源类型 0 音视频 1 音频 2 视频 3 视频或音频
     * @param stream        0 全部,1 主码流 ,2 子码流
     * @param eqType        远程存储位置 0 全部 1 主存储器 2 灾备存储器
     */
    public JSONArray queryMediaCatalog(@NotEmpty(message = MonitorException.Status.TEL_ERROR) String vehicleNumber, int channel,
                                       @NotEmpty(message = MonitorException.Status.DATE_ERROR) String startDate, @NotEmpty(message = MonitorException.Status.DATE_ERROR) String endDate,
                                       long warning, int resource, int stream, int eqType) throws MonitorException {

        List<Integer> arr1 = Arrays.asList(resources);
        List<Integer> arr2 = Arrays.asList(streams2);
        List<Integer> arr3 = Arrays.asList(eqTypes);

        if (!arr1.contains(resource)) throw new MonitorException(MonitorException.Status.RES_ERROR);
        if (!arr2.contains(stream)) throw new MonitorException(MonitorException.Status.STREAM_ERROR);
        if (!arr3.contains(eqType)) throw new MonitorException(MonitorException.Status.EQUIPMENT_ERROR);
        //格式化时间
        SimpleDateFormat format1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            format1.parse(startDate);
            format1.parse(endDate);
        } catch (ParseException e) {
            throw new MonitorException(MonitorException.Status.DATE_ERROR);
        }

        //实例化命令对象
        JT_9205 order = new JT_9205(channel, startDate, endDate, warning, resource, stream, eqType);
        Thread thread = Thread.currentThread();//当前线程
        String orderId = order.getSerialNumber(); //流水号
        logger.info("注册线程 [" + thread.getId() + "] 的回调监听器");
        ConcurrentCollection.getInstance().setThread(orderId, thread);
        try {
            logger.info("发送指令9205.消息id为 [" + orderId + "]" + "等待的线程ID为 [" + thread.getId()+"]");
            //发送获取目录指令
            jt808Service.post(order.getSerialNumber(), JT808Constant.MSG_ID.PLA_REQ_PLAYBACK_CATALOG, vehicleNumber, order.WriteToBytes());
        } catch (NullPointerException e) {
            throw new MonitorException(MonitorException.Status.NOT_ONLINE);
        }
        //要求当前线程等待10秒,等待终端数据获取
        logger.info("线程 [" + thread.getId() + "] 开始等待");
        synchronized (thread) {
            try {
                thread.wait(20000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        logger.info("线程 [" + thread.getId() + "] 等待结束,清理 线程orderId: [" + orderId+"]");
        JSONArray arr = ConcurrentCollection.getInstance().getResult(orderId);
        ConcurrentCollection.getInstance().removeAll(orderId);
        return arr;
    }



    /**
     * 下达开始远程复播指令
     *
     * @param vehicleNumber 车辆关联的手机号
     * @param ip            流媒体服务器ip
     * @param tcpPort       流媒体tcp端口
     * @param udpPort       流媒体udp端口
     * @param channel       逻辑通道号
     * @param stream        主码流,子码流
     * @param resource      音视频资源类型 0 音视频 1 音频 2 视频 3 视频或音频
     * @param local         远程存储位置 0 全部 1 主存储器 2 灾备存储器
     * @param backType      回放方式 0 正常 1 快进 2 关键帧快退  3 关键帧播放 4 单帧上传
     * @param multiple      快进或快退倍数 回放类型为1 2 时,该字段有效,否则默认0   0\无效  1\1倍 2\2倍 3\4倍 4\8倍 5\16倍
     * @param startTime     起始时间 格式 yyyy-MM-dd HH:mm:ss
     * @param endTime       截止时间 格式 yyyy-MM-dd HH:mm:ss
     */
    public String startPlayBackOrder(@NotEmpty(message = MonitorException.Status.TEL_ERROR) String vehicleNumber,
                                      @NotEmpty(message = MonitorException.Status.IP_ERROR) String ip,
                                      String tcpPort, String udpPort, int channel, int stream, int resource,
                                      int local, int backType, int multiple, String startTime, String endTime) throws MonitorException {
        List<Integer> arr1 = Arrays.asList(eqTypes);
        List<Integer> arr2 = Arrays.asList(streams2);
        List<Integer> arr3 = Arrays.asList(resources);
        List<Integer> arr4 = Arrays.asList(multiples);
        List<Integer> arr5 = Arrays.asList(backTypes);

        if (!arr1.contains(local)) throw new MonitorException(MonitorException.Status.EQUIPMENT_ERROR);
        if (!arr2.contains(stream)) throw new MonitorException(MonitorException.Status.STREAM_ERROR);
        if (!arr3.contains(resource)) throw new MonitorException(MonitorException.Status.RES_ERROR);
        if (!arr4.contains(multiple)) throw new MonitorException(MonitorException.Status.MULTIPLES_ERROR);
        if (!arr5.contains(backType)) throw new MonitorException(MonitorException.Status.BACK_TYPE_ERROR);

        //播放器客户端监控
        Vehicle vehicle = vehicleDAO.selectVehicleNumber(vehicleNumber);
        VideoMonitorReport videoMonitorReport = new VideoMonitorReport();
        videoMonitorReport.setVehicleNumber(vehicle.getNumber());
        videoMonitorReport.setChannelNumber(channel+"");
        videoMonitorReport.setMonitorType("复播");
        videoMonitorReport.setBitStream(stream == 0 ? "主码流":"子码流");
        videoMonitorReport.setBeginTime(new Date());
        videoMonitorReport.setBrowserType("IE11浏览器");
        redisCacheManager.set(vehicle.getNumber(),videoMonitorReport);


            //下发直播监控指令
            JSONObject body = new JSONObject();
            body.put("channelNumber", channel);
            body.put("simNo", vehicleNumber);
            body.put("playType", 1);
        try{
            int count = mediaPostService.postWithResult(MediaConstant.MSG_ID.PLA_REQ_ONLINE_CHANNEL, body);
            if (count!=0) {
                logger.warn("设备:" + vehicleNumber + "  通道 :" + channel + "  视频数据通道已连接,无需下发推送指令");
                return "该设备视频数据通道已连接,无需下发推送指令";
            }
        }catch (InterruptedException | ExecutionException | NullPointerException e) {
            throw new MonitorException(MonitorException.Status.MEDIA_ERROR);
        }

        try {
            //格式化时间
            SimpleDateFormat format1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            format1.parse(startTime);
            format1.parse(endTime);
            short s_tcp = Short.parseShort(tcpPort);
            short s_ucp = Short.parseShort(udpPort);
            JT_9201 order = new JT_9201(ip.length(), ip, s_tcp, s_ucp, channel, resource, stream, local, backType, multiple, startTime, endTime);
            //请求执行下达
            jt808Service.post(JT808Constant.MSG_ID.PLA_REQ_PLAYBACK, vehicleNumber, order.WriteToBytes());
            logger.info("设备:" +vehicleNumber +"  通道 :" +channel+  " 远程复播监控指令下发成功");
            return "指令下发成功";
        } catch (NumberFormatException e) {
            throw new MonitorException(MonitorException.Status.PORT_ERROR);
        } catch (NullPointerException e) {
            throw new MonitorException(MonitorException.Status.NOT_ONLINE);
        } catch (ParseException e) {
            throw new MonitorException(MonitorException.Status.DATE_ERROR);
        }
    }

    /**
     * 远程复播控制指令下达
     *
     * @param vehicleNumber
     * @param channel       逻辑通道号
     * @param control       远程复播控制 0 开始回放 1 暂停回放 2 结束回放 3 快进回放 4 关键帧快退回放 5 拖动回放 6 关键帧播放
     * @param multiple      快进快退倍数 回放控制3或4时,该字段有效,其他情况为0 0\无效 1\1倍 2\2倍 3\4倍 4\8倍 5\16倍
     * @param time          回放控制为5时,该字段有效,时间类型 ,格式为 YYMMDDHHMMSS
     * @return
     * @throws MonitorException
     */
    public String playBackControl(@NotEmpty(message = MonitorException.Status.TEL_ERROR) String vehicleNumber,
                                   Integer channel, Integer control, Integer multiple,
                                   @NotEmpty(message = MonitorException.Status.DATE_ERROR) String time) throws MonitorException {
        List<Integer> arr1 = Arrays.asList(controls);
        List<Integer> arr2 = Arrays.asList(multiples);

        if (!arr1.contains(control)) throw new MonitorException(MonitorException.Status.CONTROL_ERROR);
        if (!arr2.contains(multiple)) throw new MonitorException(MonitorException.Status.MULTIPLES_ERROR);

        try{
            //拼装控制指令
            JSONObject body = new JSONObject();
            body.put("channelNumber", channel);
            body.put("simNo", vehicleNumber);
            body.put("playType", 1);
            Integer result = mediaPostService.postWithResult(MediaConstant.MSG_ID.PLA_REQ_ONLINE_CHANNEL, body);
            if (0 == control && result !=null  && 1 < result ) {
                logger.warn("设备:" +vehicleNumber +"  通道 :" +channel+ "  视频存在多人观看,无需下发关闭指令");
                return "该通道视频存在多人观看,无需下发关闭指令";
            }
        }catch (InterruptedException | ExecutionException |NullPointerException e) {
            throw new MonitorException(MonitorException.Status.MEDIA_ERROR);
        }

        try {
            //格式化时间
            SimpleDateFormat format1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            format1.parse(time);
            JT_9202 order = new JT_9202(channel, control, multiple, time);
            //请求执行下达
            jt808Service.post(JT808Constant.MSG_ID.PLA_REQ_PLAYBACK_CONTROL, vehicleNumber, order.WriteToBytes());

            logger.info("设备:" +vehicleNumber +"  通道 :" +channel+ "  指令:" +control +"  下发成功");
            return "指令下发成功";
        } catch (NullPointerException e) {
            throw new MonitorException(MonitorException.Status.NOT_ONLINE);
        } catch (ParseException e) {
            throw new MonitorException(MonitorException.Status.DATE_ERROR);
        }
    }

    /**
     * 下达上传文件指令
     *
     * @param vehicleNumber 车载设备sim卡
     * @param ip            ftp服务器ip
     * @param port          ftp服务器端口
     * @param username      ftp服务器用户名
     * @param pwd           ftp服务器用户密码
     * @param filePath      ftp服务器文件存储地址
     * @param channel       逻辑通道号
     * @param warns         报警标识
     * @param type          音视频资源类型 0 音视频 1 音频 2 视频 3 视频或音频
     * @param stream        码流类型 0 全部 1 主码流 2 子码流
     * @param local         存储类型  0 全部 1 主存储 2 灾备存储
     * @param condition     上传条件 1 wifi上传 2LAN上传 4 3G/4G上传
     * @param startTime     上传文件起始时间 格式 yyyy-MM-dd HH:mm:ss
     * @param endTime       上传文件截止时间 格式 yyyy-MM-dd HH:mm:ss
     */
    public boolean upFile(@NotEmpty(message = MonitorException.Status.TEL_ERROR) String vehicleNumber,
                          @NotEmpty(message = MonitorException.Status.IP_ERROR) String ip,
                          String port, String username, String pwd, String filePath,
                          int channel, int warns, int type, int stream, int local, int condition,
                          String startTime, String endTime) throws MonitorException {
        List<Integer> arr2 = Arrays.asList(streams2);
        List<Integer> arr3 = Arrays.asList(resources);
        List<Integer> arr4 = Arrays.asList(eqTypes);
        List<Integer> arr5 = Arrays.asList(conditions);

        if (!arr2.contains(stream)) throw new MonitorException(MonitorException.Status.STREAM_ERROR);
        if (!arr3.contains(type)) throw new MonitorException(MonitorException.Status.RES_ERROR);
        if (!arr4.contains(local)) throw new MonitorException(MonitorException.Status.EQUIPMENT_ERROR);
        if (!arr5.contains(condition)) throw new MonitorException(MonitorException.Status.UP_CONDITION_ERROR);

        //格式化时间
        SimpleDateFormat format1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            format1.parse(startTime);
            format1.parse(endTime);
            short ftpPort = Short.parseShort(port);
            JT_9206 order = new JT_9206(ip, ftpPort, username, pwd, filePath, channel, startTime, endTime, warns, type, stream, local, condition);
            //请求执行下达
            return jt808Service.post(JT808Constant.MSG_ID.PLA_REQ_UP_FILE, vehicleNumber, order.WriteToBytes());

        } catch (NumberFormatException e) {
            throw new MonitorException(MonitorException.Status.PORT_ERROR);
        } catch (NullPointerException e) {
            throw new MonitorException(MonitorException.Status.NOT_ONLINE);
        } catch (ParseException e) {
            throw new MonitorException(MonitorException.Status.DATE_ERROR);
        }
    }

    /**
     * 文件上传控制
     *
     * @param vehicleNumber 设备内置sim卡
     * @param number        文件上传流水号
     * @param control       文件上传能控制 0暂停 1继续 2取消
     * @return
     * @throws MonitorException
     */
    public boolean upFileControl(@NotEmpty(message = MonitorException.Status.TEL_ERROR) String vehicleNumber,
                                 int number, int control) throws MonitorException {
        if (number == 0) {
            throw new MonitorException(MonitorException.Status.UP_NUMBER_ERROR);
        }
        JT_9207 order = new JT_9207(number, control);
        return jt808Service.post(JT808Constant.MSG_ID.PLA_REQ_FILE_UPLOAD_CONTROL, vehicleNumber, order.WriteToBytes());
    }


    /**
     * 查询所有本地视频目录
     *
     * @param vehicleNumber 车辆内置sim卡
     * @param channel       通道号
     * @param startTime     起始时间
     * @param endTime       终止时间
     * @param warns         报警标识
     * @return
     * @throws MonitorException
     */
    public List<Video> queryVideoList(
            @NotEmpty(message = MonitorException.Status.TEL_ERROR) String vehicleNumber, int channel,
            @NotEmpty(message = MonitorException.Status.DATE_ERROR) String startTime,
            @NotEmpty(message = MonitorException.Status.DATE_ERROR) String endTime, String warns
    ) throws MonitorException {
        //格式化时间
        SimpleDateFormat format1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            format1.parse(startTime);
            format1.parse(endTime);
            return videoDAO.queryVideoList(vehicleNumber, channel, startTime, endTime, warns);
        } catch (ParseException e) {
            throw new MonitorException(MonitorException.Status.DATE_ERROR);
        }
    }


    /**
     * 保存新的视频
     *
     * @param video
     */
    public void addVideo(@Valid Video video) {
        video.setStatus(ConstantKit.STATUS.IS_AVAILABLE);
        videoDAO.insert(video);
    }

    public static void main(String[] arge) {
        System.out.println("start");
        try {
            Thread t = Thread.currentThread();
            synchronized (t) {
                t.wait(2000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("end");
    }
}
